#pragma once

#include <iostream>
#include <pthread.h>
#include <vector>
#include <semaphore.h>
#include <cassert>

static const int gcap = 5;

template<class T>
class RingQueue
{
private:
    void P(sem_t &sem_)//P：申请资源 sem--
    {
        int n = sem_wait(&sem_);//申请资源 资源--  原子的
        assert(n == 0);
        (void)n;
    }

    void V(sem_t &sem_)//V： 唤醒资源 sem++
    {
        int n = sem_post(&sem_);//唤醒资源 资源++   原子的
        assert(n == 0);
        (void)n;
    }
public:
    RingQueue(const int &cap_ = gcap)
        :_queue(cap_)
        ,_cap(cap_)
    {
        int n = sem_init(&_spaceSem , 0 , _cap);//空间资源默认是数组重量的大小
        assert(n == 0);
        n = sem_init(&_dataSem , 0 , 0);//数据资源默认为0
        assert(n == 0);
        (void)n;

        _productorStep = _consumerStep = 0;//初始化下标都是0

        pthread_mutex_init(&_pmutex , nullptr);
        pthread_mutex_init(&_cmutex , nullptr);
    }
    void push(const T &in)
    {
        P(_spaceSem);//申请空间资源，成功继续执行，失败进行等待
        pthread_mutex_lock(&_pmutex);
        _queue[_productorStep++] = in;
        _productorStep %= _cap; 
        pthread_mutex_unlock(&_pmutex);
        V(_dataSem);//唤醒数据资源
    }

    void pop(T *out)
    {
        P(_dataSem);//申请数据资源，成功继续执行，失败进行等待
        pthread_mutex_lock(&_cmutex);
        *out = _queue[_consumerStep++];
        _consumerStep %= _cap;
        pthread_mutex_unlock(&_cmutex);
        V(_spaceSem);////唤醒空间资源
    }

    ~RingQueue()
    {
        sem_destroy(&_spaceSem);
        sem_destroy(&_dataSem);

        pthread_mutex_destroy(&_pmutex);
        pthread_mutex_destroy(&_cmutex);
    }
private:
    std::vector<T> _queue;//使用vector来实现循环队列
    int _cap;//容量
    sem_t _spaceSem;//生产者看重资源：空间资源
    sem_t _dataSem;//消费者看重的转：数据资源
    int _productorStep;//记录空间中将要申请资源的坐标（放苹果）
    int _consumerStep;//记录空间中即将唤醒资源的坐标（吃苹果）

    pthread_mutex_t _pmutex;//管理生产的锁
    pthread_mutex_t _cmutex;//管理消耗的锁
};